﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace KiDS_Bootstrap
{
	/// <summary>
	/// Klasa koja sluzi za slusanje zahteva za bootstrap, ne radi nista drugo nego sto 
	/// odradi komandu koja joj stigne i vrati odgovor
	/// Takodje drzi listu svih servenata mastera koji su se prijavili
	/// </summary>
	public unsafe class Bootstrap
	{
		private SOCKET _serverSocket;
		private readonly Dictionary<string, List<Servent>> _masters = new Dictionary<string, List<Servent>>();

		public Bootstrap(ushort port)
		{
			CreateSocket(port);
		}


		/// <summary>
		/// Kreiranje socketa
		/// </summary>
		/// <param name="port">Port na kojem ce socket slusati</param>
		/// <returns>Vraca true ako je socket uspesno kreiran i false u suprotnom</returns>
		private bool CreateSocket(ushort port)
		{
			WSAData wsaData = new WSAData();

			int startupResult = Native.WSAStartup(/*MAKEWORD(2, 2)*/514, out wsaData);
			if (startupResult == Native.SOCKET_ERROR)
			{
				Console.WriteLine("WSA start-up error code: " + wsaData.ToString());
				return false;
			}

			// [ 1 ] Set-up socket
			_serverSocket = Native.socket(AddressFamily.InterNetworkv4, SocketType.Stream,
							ProtocolType.Tcp);
			if (_serverSocket == Native.INVALID_SOCKET)
			{
				Console.WriteLine("Creating socket: " + Native.WSAGetLastError().ToString());
				return false;
			}

			// [ 2 ] Bind socket to ip adress and port of the server
			sockaddr_in sockaddr = new sockaddr_in();

			sockaddr.sin_family = Native.AF_INET;
			sockaddr.sin_addr.S_addr = Native.inet_addr(Native.INADDR_ANY);
			sockaddr.sin_port = Native.htons(port);

			int bindResult = Native.bind(_serverSocket, &sockaddr, Marshal.SizeOf(sockaddr));
			if (bindResult == Native.SOCKET_ERROR)
			{
				Console.WriteLine("Bind: " + Native.WSAGetLastError().ToString());
				return false;
			}

			return true;
		}

		/// <summary>
		/// Metod za slusanje, koji takodje i odgovara na poruke
		/// </summary>
		/// <returns>false ako ne uspeva da slusa na odredjenom portu</returns>
		public bool Listen()
		{

			Console.WriteLine("Bootstrap is listening on port " + Properties.Settings.Default.BootstrapPort + "...");
			// [ 3 ] Try to listen
			int listenResult = Native.listen(_serverSocket, 1);
			if (listenResult == Native.SOCKET_ERROR)
			{
				Console.WriteLine("Listen: " + Native.WSAGetLastError().ToString());
				return false;
			}


			// [ 4 ] Accept connection
			while (true)
			{
				
				SOCKET tempSocket = Native.SOCKET_ERROR;
				sockaddr_in temp_sockaddr = new sockaddr_in();
				int temp_len = Marshal.SizeOf(temp_sockaddr);
			//	Console.WriteLine(tempSocket);
				while (tempSocket == Native.SOCKET_ERROR)
				{
					tempSocket = Native.accept(_serverSocket, &temp_sockaddr, &temp_len);
					string address = Native.IpToText(temp_sockaddr.sin_addr.S_addr);
					if (tempSocket == -1)
						continue;
					try
					{
						Task task = new Task(() => runTask(tempSocket, address));
						task.Start();
					}
					catch
					{
						Console.WriteLine("Bootstrap task exception...");
					}
					
				}
			}
		}

		/// <summary>
		/// Novi light weight thread - Task koji ce obraditi zahtev koji je stigao bootstrapu
		/// </summary>
		/// <param name="tempSocket">socket kroz koji je zahtev stigao</param>
		/// <param name="address">adresa socketa s kog je stigao zahtev</param>
		private void runTask(SOCKET tempSocket, string address)
		{
			Command command = CommandFactory.MakeCommand(tempSocket, address, Native.RecieveMessage(tempSocket), this);
			command.DoCommmand();
			Native.closesocket(tempSocket);
		}

		/// <summary>
		/// Lista mastera koji su se prijavili bootstrapu
		/// </summary>
		public Dictionary<string, List<Servent>> Masters
		{
			get
			{
				return _masters;
			}
		}
	}
}
